package com.wh1200.mybatis.xmlless.model.component

import com.wh1200.kotlin.only
import com.wh1200.mybatis.xmlless.dialect.DialectFactory
import com.wh1200.mybatis.xmlless.model.MethodInfo
import com.wh1200.mybatis.xmlless.model.TableName
import com.wh1200.mybatis.xmlless.resolver.ColumnsResolver
import net.sf.jsqlparser.expression.*
import net.sf.jsqlparser.expression.Function
import net.sf.jsqlparser.parser.CCJSqlParserUtil
import net.sf.jsqlparser.schema.Column
import net.sf.jsqlparser.schema.Table

/**
 * 将对象属性名称替换为数据库字段名称的遍历器
 *
 * @author 吴昊
 * @date 2024/01/19
 * @version 1.0
 * @since v4.0.0
 */
class ReplacePropertyToColumnVisitor(val methodInfo: MethodInfo) : ExpressionVisitorAdapter() {

  private val middleTables = mutableSetOf<TableName>()

  override fun visit(column: Column) {
    val col = ColumnsResolver.resolveColumnByPropertyName(
        column.toString(), methodInfo, forceUseModelMappings = true
    ).only()
    middleTables.addAll(col.middleTables)
    if (col.table != null) {
      middleTables.add(col.table)
    }
    if (col.table != null) {
      column.table = Table(col.table.getAliasOrName())
    }
    val exp = CCJSqlParserUtil.parseExpression(col.column)
    if (exp is CastExpression) {
      val left = exp.leftExpression
      if (left is Parenthesis) {
        val expression = left.expression
        if (expression is JsonExpression) {
          val expr = expression.expression
          if (expr is Column) {
            expr.table = column.table
            column.columnName = exp.toString()
            column.table = null
            return
          }
        }
      }
    }
    column.columnName = col.column
  }

  override fun visitBinaryExpression(expr: BinaryExpression?) {
    super.visitBinaryExpression(expr)
  }

  override fun visit(function: Function) {
    if (function.name.lowercase() in listOf("date", "month", "year")) {
      val dialect = DialectFactory.getDialect(methodInfo.dbType)
      dialect.replaceDateFunction(function)
    }
    super.visit(function)
  }

  fun getMiddleTables(): List<TableName> {
    return middleTables.toList()
  }

}
